package com.fowo.api.mp.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fowo.api.config.AppProperties;
import com.fowo.api.mp.config.MiniProgramProperties;
import com.fowo.api.mp.model.*;
import com.fowo.api.mp.service.MiniProgramService;
import com.fowo.api.sys.entity.SysFile;
import com.fowo.api.sys.service.FileService;
import com.fowo.api.sys.service.SysFileService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.MimeTypeUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.time.Duration;

@Service
@Slf4j
@EnableConfigurationProperties({ MiniProgramProperties.class, AppProperties.class})
public class MiniProgramServiceImpl implements MiniProgramService {

    public static final String ServiceAccessTokenCacheKey = "MPAccessToken";

    @Resource
    private MiniProgramProperties miniProgramProperties;
    @Resource
    private FileService fileService;
    @Resource
    private SysFileService sysFileService;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private ObjectMapper objectMapper;

    @Override
    public String getAccessToken(boolean forceUpdate) {
        if (!forceUpdate) {
            String accessToken = (String) redisTemplate.opsForValue().get(ServiceAccessTokenCacheKey);
            if (accessToken != null) {
                return accessToken;
            }
        }

        String url = String.format(
                "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
                miniProgramProperties.getAppId(),
                miniProgramProperties.getSecret()
        );
        ServerAccessTokenResult result = restTemplate.getForObject(url, ServerAccessTokenResult.class);
        if (result != null && StringUtils.hasText(result.getAccessToken())) {
            redisTemplate.opsForValue().set(
                    ServiceAccessTokenCacheKey,
                    result.getAccessToken(),
                    Duration.ofSeconds(result.getExpiresIn())
            );
            return result.getAccessToken();
        }
        return null;
    }

    @Override
    public Code2SessionResult code2Session(String code) {
        if(!StringUtils.hasText(code)) {
            Code2SessionResult result = new Code2SessionResult();
            result.setErrCode(40029);
            result.setErrMsg("code 未设置");
            return result;
        }
        String url = String.format(
                "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
                miniProgramProperties.getAppId(),
                miniProgramProperties.getSecret(),
                code
                );
        return restTemplate.getForObject(url, Code2SessionResult.class);
    }

    @Override
    public WxResult sendMessage(WxUniformMessage message) throws Exception {
        String accessToken = getAccessToken();
        if (!StringUtils.hasText(accessToken)) {
            throw new Exception("未能获取接口调用凭证");
        }
        String url = String.format(
                "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=%s",
                accessToken
        );
        if(!StringUtils.hasText(message.getMpTemplateMsg().getAppId())) {
            if (!StringUtils.hasText(miniProgramProperties.getMpAppId())) {
                throw new Exception("配置错误，要发送小程序通用消息，需要配置 mini-program.mp-app-id (小程序关联的公众号 appId)");
            }
            message.getMpTemplateMsg().setAppId(miniProgramProperties.getMpAppId());
        }
        if(!StringUtils.hasText(message.getMpTemplateMsg().getMiniProgram().getAppId())) {
            if (!StringUtils.hasText(miniProgramProperties.getAppId())) {
                throw new Exception("配置错误，要发送小程序通用消息，需要配置 mini-program.app-id (小程序 appId)");
            }
            message.getMpTemplateMsg().getMiniProgram().setAppId(miniProgramProperties.getAppId());
        }
        return restTemplate.postForObject(url, message, WxResult.class);
    }

    @Override
    public String createQRCode(String path) throws Exception {
        String filename = String.format("wxaqrcode.%d.jpg", path.hashCode());
        final SysFile byFilename = sysFileService.getByFilename(filename);
        if (byFilename != null) {
            return byFilename.getStorageKey();
        }
        String accessToken = getAccessToken();
        if (!StringUtils.hasText(accessToken)) {
            throw new Exception("未能获取接口调用凭证");
        }
        String url = String.format(
                "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=%s",
                accessToken);
        CreateQRCodeRequest request = new CreateQRCodeRequest();
        request.setPath(path);
        byte[] bytes = restTemplate.postForObject(url, request, byte[].class);
        return handleQRCodeResult(filename, bytes);
    }

    @Override
    public String get(WxACodeRequest request) throws Exception {
        String filename = String.format("wxaqrcode.get.%d.jpg", request.getPath().hashCode());
        final SysFile byFilename = sysFileService.getByFilename(filename);
        if (byFilename != null) {
            return byFilename.getStorageKey();
        }
        String accessToken = getAccessToken();
        if (!StringUtils.hasText(accessToken)) {
            throw new Exception("未能获取接口调用凭证");
        }
        String url = String.format(
                "https://api.weixin.qq.com/wxa/getwxacode?access_token=%s",
                accessToken);
        byte[] bytes = restTemplate.postForObject(url, request, byte[].class);
        return handleQRCodeResult(filename, bytes);
    }

    @Override
    public String getUnlimited(WxACodeUnlimitedRequest request) throws Exception {
        String filename = String.format(
                "wxaqrcode.getUnlimited.%d.%d.jpg",
                request.getScene().hashCode(),
                request.getPath() == null ? 0 : request.getPath().hashCode()
        );
        final SysFile byFilename = sysFileService.getByFilename(filename);
        if (byFilename != null) {
            return byFilename.getStorageKey();
        }
        String accessToken = getAccessToken();
        if (!StringUtils.hasText(accessToken)) {
            throw new Exception("未能获取接口调用凭证");
        }
        String url = String.format(
                "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s",
                accessToken);
        byte[] bytes = restTemplate.postForObject(url, request, byte[].class);
        return handleQRCodeResult(filename, bytes);
    }

    @Override
    public PhoneInfoResult.PhoneInfo getPhoneNumber(String code) throws Exception {
        String accessToken = getAccessToken();
        if (!StringUtils.hasText(accessToken)) {
            throw new Exception("未能获取接口调用凭证");
        }
        CodeRequest request = new CodeRequest();
        request.setCode(code);
        String url = String.format(
                "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s",
                accessToken);
        PhoneInfoResult phoneInfoResult = restTemplate.postForObject(url, request, PhoneInfoResult.class);
        if (phoneInfoResult == null) {
            throw new Exception("未能获取手机号信息");
        }
        if (phoneInfoResult.getPhoneInfo() != null) {
            return phoneInfoResult.getPhoneInfo();
        }
        throw new Exception(phoneInfoResult.getErrMsg());
    }

    @Override
    public WxResult sendSubscribeMessage(WxSubscribeMessage message) throws Exception {
        String accessToken = getAccessToken();
        if (!StringUtils.hasText(accessToken)) {
            throw new Exception("未能获取接口调用凭证");
        }
        String url = String.format(
                "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%s",
                accessToken
        );
        return restTemplate.postForObject(url, message, WxResult.class);
    }

    @Data
    static class CodeRequest {
        private String code;
    }

    private String handleQRCodeResult(String filename, byte[] bytes) throws Exception {
        if (bytes == null || bytes.length == 0) {
            throw new Exception("请求微信接口返回空结果");
        }
        if (bytes[0] == '{') {
            // JSON 结果
            final WxResult wxResult = objectMapper.readValue(bytes, WxResult.class);
            log.error("生成二维码失败返回：{}", wxResult);
            throw new Exception(wxResult.getErrMsg());
        }

        // 上传生成的二维码
        SysFile sysFile = fileService.upload(
                filename,
                MimeTypeUtils.IMAGE_JPEG_VALUE,
                new ByteArrayInputStream(bytes),
                (long) bytes.length,
                null);
        return sysFile.getStorageKey();
    }

    @Data
    static class CreateQRCodeRequest {
        private String path;
        private Integer width = 430;
    }
}
